package com.momoc.mrpc;

import com.momoc.mrpc.annotation.EnabledMRpc;
import com.momoc.mrpc.annotation.MRpcReference;
import com.momoc.mrpc.annotation.MRpcService;
import com.momoc.mrpc.register.RegisterContext;
import com.momoc.mrpc.register.definition.MRpcRegDefinition;
import com.momoc.mrpc.rpc.ScanningReferenceServiceInfo;
import com.momoc.mrpc.rpc.init.Start;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * @author momoc
 * @version 1.0
 * @className MRpcScanningImport
 * @description 注册带有MRpcService到容器中
 * @date 2022/7/25 5:27 下午
 */
@Slf4j
public class MRpcScanningImport implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    // 实现EnvironmentAware接口，可以拿到系统的环境变量信息
    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnabledMRpc.class.getName());
        Object scanBasePackages = annotationAttributes.get("baseScans");
        String className = importingClassMetadata.getClassName();

        //没有默认扫描当前启动类路径
        String scanPath = className.substring(0, className.lastIndexOf("."));
        if (scanBasePackages == null){
            scanBasePackages = new String[]{scanPath};
        }
        if (scanBasePackages instanceof String[]){
            String[] scansPath = (String[]) scanBasePackages;
            for (String path : scansPath) {
                try {
                    //获取自定义注解信息
                    List<Class<?>> candidateClasses = findCandidateClasses(path);
                    //注册到bean容器中
                    registerBeanDefinitions(candidateClasses, registry);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private String convertPath(String path) {
        return StringUtils.replace(path, ".", "/");
    }
    // 将指定包下面的符合条件的类返回
    private List<Class<?>> findCandidateClasses(String basePackage) throws IOException {
        List<Class<?>> candidates = new ArrayList<Class<?>>();
        // classpath*:com/scorpios/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                convertPath(basePackage) + '/' + this.DEFAULT_RESOURCE_PATTERN;
        ResourceLoader resourceLoader = new DefaultResourceLoader();
        MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(resourceLoader);
        Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources(packageSearchPath);
        for (Resource resource : resources) {
            MetadataReader reader = readerFactory.getMetadataReader(resource);
            String className = reader.getClassMetadata().getClassName();
            try {
                Class<?> aClass = ClassUtils.forName(className, this.getClass().getClassLoader());
                for (Field declaredField : aClass.getDeclaredFields()) {
                    MRpcReference annotation = declaredField.getAnnotation(MRpcReference.class);
                    if (annotation == null){
                        continue;
                    }
                    ScanningReferenceServiceInfo.referenceServiceMap.put(aClass.getName(), aClass);
                }
                MRpcService annotation = aClass.getAnnotation(MRpcService.class);
                if (annotation == null){
                    continue;
                }
                log.debug("扫描到符合要求的类：" + aClass.getName());
                candidates.add(aClass);
            } catch (ClassNotFoundException e) {
                log.info("未找到指定类,{}", className);
            }
        }

        return candidates;
    }


    private void registerBeanDefinitions(List<Class<?>> internalClasses, BeanDefinitionRegistry registry) {
        for (Class<?> clazz : internalClasses) {
//            String beanName = ClassUtils.getShortNameAsProperty(clazz);
//            RootBeanDefinition rbd = new RootBeanDefinition(clazz);
            //注册bena信息
//            registry.registerBeanDefinition(beanName, rbd);
            ArrayList<String> methods = new ArrayList<>();
            for (Method method : clazz.getDeclaredMethods()) {
                methods.add(method.getName());
            }
            //生成注册信息
            MRpcRegDefinition mRpcRegDefinition = new MRpcRegDefinition(clazz.getName(), methods);
            MRpcService annotation = clazz.getAnnotation(MRpcService.class);
            mRpcRegDefinition.setInterfaceClass(annotation.interfaceClassName());
            RegisterContext.rpcNameServiceMap.put(annotation.interfaceClassName().getName(), mRpcRegDefinition);

            mRpcRegDefinition = new MRpcRegDefinition(clazz.getName(), methods);
            mRpcRegDefinition.setInterfaceClass(clazz);
            RegisterContext.registerServiceMap.put(clazz.getName(), mRpcRegDefinition);
            RegisterContext.rpcNameServiceMap.put(clazz.getName(), mRpcRegDefinition);

        }
    }

}
